/*
 * \brief  Implementation of the Thread API
 * \author Norman Feske
 * \author Christian Helmuth
 * \date   2006-07-17
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#include <base/thread.h>
#include <base/printf.h>
#include <base/env.h>
#include <base/sleep.h>

using namespace Genode;

/* DEBUGGING */
static bool verbose = false;


/**
 * Thread-startup argument frame
 */
Thread_base *Thread_base::_startup_arg;
Lock         Thread_base::_startup_arg_lock;


void Thread_base::_thread_start(void *arg)
{
	/* read and release thread-argument frame */
	Thread_base *me = _startup_arg;
	_startup_arg_lock.unlock();

	if (verbose)
		PDBG("starting new thread at 0x%p", me);

	me->entry();

	if (verbose)
		PDBG("returned from entry function.");

	/* wait to become physically killed */
	sleep_forever();
}


void Thread_base::_start(void *sp, const char *name)
{
	/* push this-pointer to the stack of the new thread */
	void **stack = reinterpret_cast<void **>(sp);
	stack[0] = this;

	/* create and start thread */
	_thread_cap = env()->cpu_session()->create_thread(name);

	env()->pd_session()->bind_thread(_thread_cap);

	Thread_capability pager_cap;
	try {
		pager_cap = env()->rm_session()->add_client(_thread_cap);
	} catch (...) {

		/*
		 * FIXME  We should reflect 'add_client' exception types
		 *        at the 'Thread_base' interface
		 */
		return;
	}

	env()->cpu_session()->set_pager(_thread_cap, pager_cap);

	/* pass thread argument via startup-argument frame to new thread */
	_startup_arg_lock.lock();
	_startup_arg = this;

	int res = env()->cpu_session()->start(_thread_cap,
	                                      (addr_t)_thread_start,
	                                      (addr_t)&stack[-1]);

	/* free argument lock on failed startup */
	if (res)
		_startup_arg_lock.unlock();
}


void Thread_base::cancel_blocking()
{
	env()->cpu_session()->cancel_blocking(_thread_cap);
}


Thread_base::~Thread_base()
{
	env()->cpu_session()->kill_thread(_thread_cap);
}
